#include "DBLayer.h"
#include "Utils.h"
#include "LogManager.h"
#include "Defines.h"
#include <mysql++/custom.h>
#include <mysql/errmsg.h>
#include <zlib/zlib.h>

DBLayer::DBLayer(void)
{
    InitializeCriticalSection(&criticalSection);
    connection = new mysqlpp::Connection(mysqlpp::use_exceptions);
}

DBLayer::~DBLayer(void)
{
    delete connection;
    DeleteCriticalSection(&criticalSection);
}

void DBLayer::ConvertStringToHexString(const unsigned char* buffer,
                                       unsigned long ulBufferSize,
                                       string& szHexString)
{
    szHexString += "0x";
    if (ulBufferSize > 0)
    {
        char temp[3];
        temp[2] = 0;
        for (unsigned long i = 0; i < ulBufferSize; i++)
        {
            sprintf(temp, "%02x", (unsigned long)*(buffer+i));
            szHexString += temp;
        }
    }
    else
    {
        szHexString += "00";
    }
}

bool DBLayer::BeginTransaction()
{
    bool bResult = false;
    try
    {
        mysqlpp::Query query = connection->query();
        query.exec("SET AUTOCOMMIT = 0;");
        query.exec("START TRANSACTION");
        bResult = true;
    }
    catch (const mysqlpp::Exception& exc)
    {
        LOGT(("DBLayer::BeginTransaction. MySQL exception occurred. Message: %s\n", exc.what()));
        NeedReconnect(connection->errnum());
    }
    catch (std::exception& exc)
    {
        LOGT(("DBLayer::BeginTransaction. Std exception occurred. Message: %s\n", exc.what()));
    }    
    return bResult;
}

bool DBLayer::CommitTransaction()
{
    bool bResult = false;
    try
    {
        mysqlpp::Query query = connection->query();
        query.exec("COMMIT");
        query.exec("SET AUTOCOMMIT = 1;");
        bResult = true;
    }
    catch (const mysqlpp::Exception& exc)
    {
        LOGT(("DBLayer::CommitTransaction. MySQL exception occurred. Message: %s\n", exc.what()));
        NeedReconnect(connection->errnum());
    }
    catch (std::exception& exc)
    {
        LOGT(("DBLayer::CommitTransaction. Std exception occurred. Message: %s\n", exc.what()));
    }    
    return bResult;
}

bool DBLayer::RollBackTransaction()
{
    bool bResult = false;
    try
    {
        mysqlpp::Query query = connection->query();
        query.exec("ROLLBACK");
        query.exec("SET AUTOCOMMIT = 1;");
        bResult = true;
    }
    catch (const mysqlpp::Exception& exc)
    {
        LOGT(("DBLayer::RollBackTransaction. MySQL exception occurred. Message: %s\n", exc.what()));
        NeedReconnect(connection->errnum());
    }
    catch (std::exception& exc)
    {
        LOGT(("DBLayer::RollBackTransaction. Std exception occurred. Message: %s\n", exc.what()));
    }    
    return bResult;
}

bool DBLayer::Connect(  const string& db,
                        const string& host,
                        const unsigned int& port,
                        const string& username,
                        const string& password)
{
    bool bResult = false;
    szDBName = db;
    szHost = host;
    uiPort = port;
    szUsername = username;
    szPassword = password;
    try
    {
        if (connection->connect(db.c_str(), host.c_str(), username.c_str(), password.c_str(), port))
        {
            mysqlpp::Query query = connection->query();
            if (query.exec("SET NAMES utf8;"))
                LOGT(("DBLayer::Connect. Set UTF-8 locale competed\n"));
            if (query.exec("SET AUTOCOMMIT = 1;"))
                LOGT(("DBLayer::Connect. Set AUTOCOMMIT = 1 competed\n"));
            bResult = true;
        }
    }
    catch (mysqlpp::Exception& exc)
    {
        LOGT(("DBLayer::Connect. MySQL exception occurred. Message: %s\n", exc.what()));
    }
    catch (std::exception& exc)
    {
        LOGT(("DBLayer::Connect. Std exception occurred. Message: %s\n", exc.what()));
    }
    return bResult;
}

bool DBLayer::Reconnect(  const string& db,
                          const string& host,
                          const unsigned int& port,
                          const string& username,
                          const string& password)
{
    bool bResult = false;
    try
    {
        delete connection;
        connection = new mysqlpp::Connection(mysqlpp::use_exceptions);
        if (connection->connect(db.c_str(), host.c_str(), username.c_str(), password.c_str(), port))
        {
            LOGT(("DBLayer::Reconnect. Reconnect completed\n"));
            mysqlpp::Query query = connection->query();
            if (query.exec("SET NAMES utf8;"))
                LOGT(("DBLayer::Reconnect. Set UTF-8 locale competed\n"));
            if (query.exec("SET AUTOCOMMIT = 1;"))
                LOGT(("DBLayer::Reconnect. Set AUTOCOMMIT=1 competed\n"));
            bResult = true;
        }        
    }
    catch (mysqlpp::Exception& exc)
    {
        LOGT(("DBLayer::Reconnect. MySQL exception occurred. Message: %s\n", exc.what()));
    }
    catch (std::exception& exc)
    {
        LOGT(("DBLayer::Reconnect. Std exception occurred. Message: %s\n", exc.what()));
    }
    return bResult;
}

void DBLayer::NeedReconnect(const int& err)
{
    if (err == CR_SERVER_LOST || err == CR_SERVER_GONE_ERROR)
    {
        LOGT(("DBLayer::NeedReconnect. Try reconnect\n"));
        Reconnect(szDBName, szHost, uiPort, szUsername, szPassword);
    }
}

bool DBLayer::GetMonitorInfo(   unsigned int& uiMonitorID,
                                unsigned int& uiAccountID,
                                string& szMonitorTitle,
                                unsigned int& uiMonitorStatus, 
                                string& szUrl,
                                string& szReferenceUrl,
                                unsigned char& ucUserAgentType,
                                string& szUserAgent,
                                string& szHttpAuthInfo,
                                string& szCookiesData,
                                string& szFormContent,
                                string& szProxyAddrInfo,
                                string& szProxyAuthInfo,                                
                                string& szUserCharset,
                                string& szXPath,
                                unsigned int& uiTriggerType, 
                                string& szTriggerParams,
                                int& iHLFontColor,
                                int& iHLBkColor,
                                unsigned int& uiHLFontStyle,
                                string& szAlertEmails,
                                string& szAlertEmailsBcc,
                                unsigned int& uiAlertType,
                                unsigned char& ucAlertFormat,
                                int& iAlertAtTime,
                                int& iTimeZoneOffset)
{
    bool bResult = false;
    EnterCriticalSection(&criticalSection);
    uiMonitorID = 0;
    uiAccountID = 0;
    try
    {
        mysqlpp::Query query = connection->query();
        query << "SELECT monitors.id, account_id, title, status, url, reference_url, http_user_agent_type, http_user_agent_custom, ";
        query << "http_auth_user, http_auth_password, http_cookies, http_form_content, ";
        query << "proxy_addr, proxy_port, proxy_auth_user, proxy_auth_password, user_charset, xpath, ";
        query << "trigger_type, trigger_params, highlight_font_color, highlight_bk_color, highlight_font_style, ";
        query << "alert_emails, email_bcc, alert_type, def_alert_format, alert_at_time, timezone_offset ";
        query << "FROM monitors INNER JOIN accounts ON account_id = accounts.id WHERE status = " << (MONITOR_STATUS_ACTIVE | MONITOR_STATUS_APPROVED);
        query << " AND ((last_update + INTERVAL frequency SECOND <= UTC_TIMESTAMP() AND frequency <> 86400) OR ";
        query << "(DATE(last_update + INTERVAL timezone_offset SECOND) < DATE(UTC_TIMESTAMP() + INTERVAL timezone_offset SECOND) AND DATE(UTC_TIMESTAMP() + INTERVAL timezone_offset SECOND) + INTERVAL monitor_at_time SECOND <= UTC_TIMESTAMP()+ INTERVAL timezone_offset SECOND AND frequency = 86400))";
        query << " AND expire > UTC_TIMESTAMP()";        
        std::vector<unsigned int>::iterator iter;
        for (iter = monitors_cache.begin(); iter != monitors_cache.end(); iter++)
            query << " AND monitors.id != " << *iter;
        query << " LIMIT 1";
        //LOGT(("DBLayer::GetMonitorInfo. %s\n", query.str().c_str()));
        mysqlpp::Result res = query.store();
        if (!res.empty())
        {
            string szHttpAuthUser;
            string szHttpAuthPassword;
            string szProxyAddr;
            string szProxyPort;
            string szProxyAuthUser;
            string szProxyAuthPassword;

            mysqlpp::Row row = res.fetch_row();
            uiMonitorID = atol(row.raw_data(0));
            uiAccountID = atol(row.raw_data(1));
            szMonitorTitle = row.raw_data(2);
            uiMonitorStatus = atol(row.raw_data(3));
            szUrl = row.raw_data(4);
            szReferenceUrl = row.raw_data(5);
            ucUserAgentType = (unsigned char)atol(row.raw_data(6));
            szUserAgent = row.raw_data(7);
            szHttpAuthUser = row.raw_data(8);
            szHttpAuthPassword = row.raw_data(9);
            if (szHttpAuthUser.size())
            {
                szHttpAuthInfo = szHttpAuthUser;
                szHttpAuthInfo += ":";
                szHttpAuthInfo += szHttpAuthPassword;
            }
            szCookiesData = row.raw_data(10);
            szFormContent = row.raw_data(11);
            szProxyAddr = row.raw_data(12);
            szProxyPort = row.raw_data(13);
            if (szProxyAddr.size())
            {
                szProxyAddrInfo = szProxyAddr;
                szProxyAddrInfo += ":";
                szProxyAddrInfo += szProxyPort;
            }
            szProxyAuthUser = row.raw_data(14);
            szProxyAuthPassword = row.raw_data(15);
            if (szProxyAuthUser.size())
            {
                szProxyAuthInfo = szProxyAuthUser;
                szProxyAuthInfo += ":";
                szProxyAuthInfo += szProxyAuthPassword;
            }
            szUserCharset = row.raw_data(16);
            szXPath = row.raw_data(17);
            uiTriggerType = atol(row.raw_data(18));
            szTriggerParams = row.raw_data(19);
            iHLFontColor = atol(row.raw_data(20));
            iHLBkColor = atol(row.raw_data(21));
            uiHLFontStyle = atol(row.raw_data(22));
            szAlertEmails = row.raw_data(23);
            szAlertEmailsBcc = row.raw_data(24);
            uiAlertType = atol(row.raw_data(25));
            ucAlertFormat = (unsigned char)atol(row.raw_data(26));
            iAlertAtTime = atol(row.raw_data(27));
            iTimeZoneOffset = atol(row.raw_data(28));
            
            monitors_cache.push_back(uiMonitorID);
            bResult = true;
        }
    }
    catch (const mysqlpp::Exception& exc)
    {
        LOGT(("DBLayer::GetMonitorInfo. MySQL exception occurred. Message: %s\n", exc.what()));
        NeedReconnect(connection->errnum());
    }
    catch (std::exception& exc)
    {
        LOGT(("DBLayer::GetMonitorInfo. Std exception occurred. Message: %s\n", exc.what()));
    }
    LeaveCriticalSection(&criticalSection);
    return bResult;
}

bool DBLayer::GetEmailInfo( unsigned int& uiEmailID,
                            unsigned int& uiMonitorID,
                            string& szEmailFrom,
                            vector<string>& recipients,                            
                            string& szEmailBody)
{
    bool bResult = false;
    EnterCriticalSection(&criticalSection);
    uiEmailID = 0;
    uiMonitorID = 0;
    try
    {
        mysqlpp::Query query = connection->query();
        query << "SELECT id, monitor_id, email_from, alert_emails, email_content, email_content_size, email_content_is_compressed ";        
        query << "FROM emails_storage WHERE send_status = " << EMAIL_STATUS_NEED_SEND << " AND send_time <= UTC_TIMESTAMP()";
        std::vector<unsigned int>::iterator iter;
        for (iter = emails_cache.begin(); iter != emails_cache.end(); iter++)
            query << " AND id != " << *iter;
        query << " LIMIT 1";
        mysqlpp::Result res = query.store();
        if (!res.empty())
        {
            mysqlpp::Row row = res.fetch_row();
            uiEmailID = atol(row.raw_data(0));
            uiMonitorID = atol(row.raw_data(1));
            szEmailFrom = row.raw_data(2);
            string szAlertEmails = row.raw_data(3);
            StringToWords(szAlertEmails, ' ', recipients);
            unsigned long *size = res.fetch_lengths();
            unsigned long ulEmailSize = size[4];
            const char* email = row.raw_data(4);
            unsigned long ulSrcEmailSize = atol(row.raw_data(5));
            bool bCompressed = atol(row.raw_data(6));
            if (bCompressed)
            {
                szEmailBody.resize(ulSrcEmailSize);
                int result = uncompress((Bytef*)szEmailBody.c_str(), &ulSrcEmailSize, (const Bytef*)email, ulEmailSize);
                if (result == Z_OK)
                {
                    bResult = true;
                    emails_cache.push_back(uiEmailID);
                }
                else
                {
                    LOGT(("DBLayer::GetEmailInfo. Uncompression failed. Zlib return: %i\n", result));
                    szEmailBody = "";
                }
            }
            else
            {
                szEmailBody = email;
                emails_cache.push_back(uiEmailID);
                bResult = true;
            }
        }
    }
    catch (const mysqlpp::Exception& exc)
    {
        LOGT(("DBLayer::GetEmailInfo. MySQL exception occurred. Message: %s\n", exc.what()));
        NeedReconnect(connection->errnum());
    }
    catch (std::exception& exc)
    {
        LOGT(("DBLayer::GetEmailInfo. Std exception occurred. Message: %s\n", exc.what()));
    }
    LeaveCriticalSection(&criticalSection);
    return bResult;
}

bool DBLayer::GetMonitorTriggerInfo(    const unsigned int& uiMonitorID,
                                        string& szXPath,
                                        unsigned int& uiTriggerType, 
                                        string& szTriggerParams,
                                        string& szUserCharset)
{
    bool bResult = false;     
    EnterCriticalSection(&criticalSection);
    try
    {
        mysqlpp::Query query = connection->query();
        query << "SELECT xpath, trigger_type, trigger_params, user_charset ";
        query << "FROM monitors WHERE id = " << uiMonitorID;
        mysqlpp::Result res = query.store();
        if (!res.empty())
        {
            mysqlpp::Row row = res.fetch_row();
            szXPath = row.raw_data(0);
            uiTriggerType = atol(row.raw_data(1));
            szTriggerParams = row.raw_data(2);
            szUserCharset = row.raw_data(3);
            bResult = true;
        }
    }
    catch (const mysqlpp::Exception& exc)
    {
        LOGT(("DBLayer::GetMonitorTriggerInfo. MySQL exception occurred. Message: %s\n", exc.what()));
        NeedReconnect(connection->errnum());
    }
    catch (std::exception& exc)
    {
        LOGT(("DBLayer::GetMonitorTriggerInfo. Std exception occurred. Message: %s\n", exc.what()));
    }
    LeaveCriticalSection(&criticalSection);
    return bResult;
}

bool DBLayer::GetMonitorTitleInfo(  const unsigned int& uiMonitorID,
                                    string& szUrl,
                                    string& szTitle)
{
    bool bResult = false;     
    EnterCriticalSection(&criticalSection);
    try
    {
        mysqlpp::Query query = connection->query();
        query << "SELECT url, title FROM monitors WHERE id = " << uiMonitorID;
        mysqlpp::Result res = query.store();
        if (!res.empty())
        {
            mysqlpp::Row row = res.fetch_row();
            szUrl = row.raw_data(0);
            szTitle = row.raw_data(1);            
            bResult = true;
        }
    }
    catch (const mysqlpp::Exception& exc)
    {
        LOGT(("DBLayer::GetMonitorTitleInfo. MySQL exception occurred. Message: %s\n", exc.what()));
        NeedReconnect(connection->errnum());
    }
    catch (std::exception& exc)
    {
        LOGT(("DBLayer::GetMonitorTitleInfo. Std exception occurred. Message: %s\n", exc.what()));
    }
    LeaveCriticalSection(&criticalSection);
    return bResult;
}

bool DBLayer::GetMonitorEmailInfo(  const unsigned int& uiMonitorID,
                                    string& szEmail,
                                    string& szEmailBcc,
                                    unsigned char& ucContentType)
{
    bool bResult = false;     
    EnterCriticalSection(&criticalSection);
    try
    {
        mysqlpp::Query query = connection->query();
        query << "SELECT alert_emails, email_bcc, def_alert_format ";
        query << "FROM monitors, accounts WHERE monitors.account_id = accounts.id AND monitors.id = " << uiMonitorID;
        mysqlpp::Result res = query.store();
        if (!res.empty())
        {
            mysqlpp::Row row = res.fetch_row();
            szEmail = row.raw_data(0);
            szEmailBcc = row.raw_data(1);
            ucContentType = (unsigned char)atol(row.raw_data(2));
            bResult = true;
        }
    }
    catch (const mysqlpp::Exception& exc)
    {
        LOGT(("DBLayer::GetMonitorEmailInfo. MySQL exception occurred. Message: %s\n", exc.what()));
        NeedReconnect(connection->errnum());
    }
    catch (std::exception& exc)
    {
        LOGT(("DBLayer::GetMonitorHLInfo. Std exception occurred. Message: %s\n", exc.what()));
    }
    LeaveCriticalSection(&criticalSection);
    return bResult;
}

bool DBLayer::GetAccountEmailInfo(  const unsigned int& uiAccountID,
                                    string& szEmail,
                                    string& szEmailBcc,
                                    unsigned char& ucContentType)
{
    bool bResult = false;     
    EnterCriticalSection(&criticalSection);
    try
    {
        mysqlpp::Query query = connection->query();
        query << "SELECT email, email_bcc, def_alert_format ";
        query << "FROM accounts WHERE id = " << uiAccountID;
        mysqlpp::Result res = query.store();
        if (!res.empty())
        {
            mysqlpp::Row row = res.fetch_row();
            szEmail = row.raw_data(0);
            szEmailBcc = row.raw_data(1);
            ucContentType = (unsigned char)atol(row.raw_data(2));
            bResult = true;
        }
    }
    catch (const mysqlpp::Exception& exc)
    {
        LOGT(("DBLayer::GetAccountEmailInfo. MySQL exception occurred. Message: %s\n", exc.what()));
        NeedReconnect(connection->errnum());
    }
    catch (std::exception& exc)
    {
        LOGT(("DBLayer::GetMonitorHLInfo. Std exception occurred. Message: %s\n", exc.what()));
    }
    LeaveCriticalSection(&criticalSection);
    return bResult;
}

bool DBLayer::GetLastDownloadedResource( const unsigned int& uiMonitorID,
                                         unsigned int& uiResourceID,
                                         string& szResource,
                                         string& szResponseHeader,
                                         string& szEffectiveUrl)
{
    bool bResult = false;
    EnterCriticalSection(&criticalSection);
    try
    {
        mysqlpp::Query query = connection->query();
        query << "SELECT id, effective_url, resource, resource_src_size, resource_is_compressed, http_response_header FROM resources_storage ";
        query << "WHERE monitor_id = " << uiMonitorID << " AND curl_code = 0 AND http_response_code = " << HTTP_STATUS_OK << " AND resource IS NOT NULL ORDER BY download_dt DESC, id DESC LIMIT 1";
        mysqlpp::Result res = query.store();
        if (!res.empty())
        {
            mysqlpp::Row row = res.fetch_row();
            uiResourceID = atol(row.raw_data(0));
            szEffectiveUrl = row.raw_data(1);
            unsigned long *size = res.fetch_lengths();
            unsigned long ulResourceSize = size[2];
            const char* resource = row.raw_data(2);
            unsigned long ulSrcResourceSize = atol(row.raw_data(3));
            bool bCompressed = atol(row.raw_data(4));
            szResponseHeader = row.raw_data(5);
            if (bCompressed)
            {
                szResource.resize(ulSrcResourceSize);
                int result = uncompress((Bytef*)szResource.c_str(), &ulSrcResourceSize, (const Bytef*)resource, ulResourceSize);
                if (result == Z_OK)
                {
                    bResult = true;
                }
                else
                {
                    LOGT(("DBLayer::GetLastDownloadedResource. Uncompression failed. Zlib return: %i\n", result));
                }
            }
            else
            {
                szResource = resource;
                bResult = true;
            }
        }
        else
        {
            LOGT(("DBLayer::GetLastDownloadedResource. MonitorID: %u. Nothing found\n", uiMonitorID));
            bResult = true;
        }
    }
    catch (const mysqlpp::Exception& exc)
    {
        LOGT(("DBLayer::GetLastDownloadedResource. MySQL exception occurred. Message: %s\n", exc.what()));
        NeedReconnect(connection->errnum());
    }
    catch (std::exception& exc)
    {
        LOGT(("DBLayer::GetLastDownloadedResource. Std exception occurred. Message: %s\n", exc.what()));
    }
    LeaveCriticalSection(&criticalSection);
    return bResult;
}

bool DBLayer::GetDownloadedResource(    const unsigned int& uiMonitorID,
                                        const unsigned int& uiResourceID,
                                        unsigned int& uiRealResourceID,
                                        string& szResource,
                                        string& szResponseHeader,
                                        string& szEffectiveUrl)
{
    bool bResult = false;
    EnterCriticalSection(&criticalSection);
    try
    {
        mysqlpp::Query query = connection->query();
        query << "SELECT id, effective_url, resource, resource_src_size, resource_is_compressed, http_response_header FROM resources_storage ";
        query << "WHERE monitor_id = " << uiMonitorID << " AND resource IS NOT NULL AND id <= " << uiResourceID;
        query << " AND EXISTS (SELECT id FROM resources_storage WHERE id = " << uiResourceID << ")";
        query << " ORDER BY id DESC LIMIT 1";
        mysqlpp::Result res = query.store();
        if (!res.empty())
        {
            mysqlpp::Row row = res.fetch_row();
            uiRealResourceID = atol(row.raw_data(0));
            szEffectiveUrl = row.raw_data(1);
            unsigned long *size = res.fetch_lengths();
            unsigned long ulResourceSize = size[2];
            const char* resource = row.raw_data(2);
            unsigned long ulSrcResourceSize = atol(row.raw_data(3));
            bool bCompressed = atol(row.raw_data(4));
            szResponseHeader = row.raw_data(5);
            if (bCompressed)
            {
                szResource.resize(ulSrcResourceSize);
                int result = uncompress((Bytef*)szResource.c_str(), &ulSrcResourceSize, (const Bytef*)resource, ulResourceSize);
                if (result == Z_OK)
                {
                    bResult = true;
                }
                else
                {
                    LOGT(("DBLayer::GetDownloadedResource. Uncompression failed. Zlib return: %i\n", result));
                }
            }
            else
            {
                szResource = resource;
                bResult = true;
            }
        }
        else
        {
            LOGT(("DBLayer::GetDownloadedResource. MonitorID: %u. ResourceID: %u. Nothing found\n", uiMonitorID, uiResourceID));
            bResult = true;
        }
    }
    catch (const mysqlpp::Exception& exc)
    {
        LOGT(("DBLayer::GetDownloadedResource. MySQL exception occurred. Message: %s\n", exc.what()));
        NeedReconnect(connection->errnum());
    }
    catch (std::exception& exc)
    {
        LOGT(("DBLayer::GetDownloadedResource. Std exception occurred. Message: %s\n", exc.what()));
    }
    LeaveCriticalSection(&criticalSection);
    return bResult;
}

bool DBLayer::GetDiffCache( const unsigned int& uiResourceID1,
                            const unsigned int& uiResourceID2,
                            string& szDiffCache,
                            bool& bDiffCacheFinded)
{
    bool bResult = false;
    bDiffCacheFinded = false;
    EnterCriticalSection(&criticalSection);
    try
    {
        mysqlpp::Query query = connection->query();
        query << "SELECT value FROM diff_cache ";
        query << "WHERE resource1_id = " << uiResourceID1 << " AND resource2_id = " << uiResourceID2;
        mysqlpp::Result res = query.store();
        if (!res.empty())
        {
            mysqlpp::Row row = res.fetch_row();
            szDiffCache = row.raw_data(0);
            bDiffCacheFinded = true;
            bResult = true;
        }
        else
        {
            LOGT(("DBLayer::GetDiffCache. ResourceID1: %u. ResourceID2: %u. Nothing found\n", uiResourceID1, uiResourceID2));
            bResult = true;
        }
    }
    catch (const mysqlpp::Exception& exc)
    {
        LOGT(("DBLayer::GetDiffCache. MySQL exception occurred. Message: %s\n", exc.what()));
        NeedReconnect(connection->errnum());
    }
    catch (std::exception& exc)
    {
        LOGT(("DBLayer::GetDiffCache. Std exception occurred. Message: %s\n", exc.what()));
    }
    LeaveCriticalSection(&criticalSection);
    return bResult;
}

bool DBLayer::GetFeed(  const unsigned int& uiMonitorID,
                        const unsigned int& uiFeedType,
                        string& szFeed)
{
    bool bResult = false;
    EnterCriticalSection(&criticalSection);
    try
    {
        mysqlpp::Query query = connection->query();
        query << "SELECT feed FROM feeds ";
        query << "WHERE monitor_id = " << uiMonitorID << " AND type = " << uiFeedType;
        mysqlpp::Result res = query.store();
        if (!res.empty())
        {
            mysqlpp::Row row = res.fetch_row();
            szFeed = row.raw_data(0);
            bResult = true;
        }
        else
        {
            LOGT(("DBLayer::GetFeed. MonitorID: %u. Nothing found\n", uiMonitorID));
            bResult = true;
        }
    }
    catch (const mysqlpp::Exception& exc)
    {
        LOGT(("DBLayer::GetFeed. MySQL exception occurred. Message: %s\n", exc.what()));
        NeedReconnect(connection->errnum());
    }
    catch (std::exception& exc)
    {
        LOGT(("DBLayer::GetFeed. Std exception occurred. Message: %s\n", exc.what()));
    }
    LeaveCriticalSection(&criticalSection);
    return bResult;
}

bool DBLayer::GetEmailTemplate( const string& szTemplateName,
                                string& szEmailSubject,
                                string& szTemplateText,
                                string& szEmailFrom,
                                string& szEmailTo,
                                string& szEmailCc,
                                string& szEmailBcc)
{
    bool bResult = false;
    EnterCriticalSection(&criticalSection);
    try
    {
        mysqlpp::Query query = connection->query();
        query << "SELECT email_subject, template_text, email_from, email_to, email_cc, email_bcc FROM emails_templates WHERE template_name = '" << szTemplateName << "'";
        mysqlpp::Result res = query.store();
        if (!res.empty())
        {
            mysqlpp::Row row = res.fetch_row();
            szEmailSubject = row.raw_data(0);
            szTemplateText = row.raw_data(1);
            szEmailFrom = row.raw_data(2);
            szEmailTo = row.raw_data(3);
            szEmailCc = row.raw_data(4);
            szEmailBcc = row.raw_data(5);
            bResult = true;
        }
        else
        {
            LOGT(("DBLayer::GetEmailTemplate. Template %s not found %u\n", szTemplateName.c_str()));
            bResult = false;
        }        
    }
    catch (const mysqlpp::Exception& exc)
    {
        LOGT(("DBLayer::GetEmailTemplate. MySQL exception occurred. Message: %s\n", exc.what()));
        NeedReconnect(connection->errnum());
    }
    catch (std::exception& exc)
    {
        LOGT(("DBLayer::GetEmailTemplate. Std exception occurred. Message: %s\n", exc.what()));
    }
    LeaveCriticalSection(&criticalSection);
    return bResult;
}

bool DBLayer::GetAllNotActivatedAccounts(   const unsigned long& ulPeriod,
                                            vector<unsigned int>& accounts)
{
    bool bResult = false;
    EnterCriticalSection(&criticalSection);
    try
    {
        mysqlpp::Query query = connection->query();
        query << "SELECT id FROM accounts WHERE activated = 0 AND created + INTERVAL " << ulPeriod << " DAY < UTC_TIMESTAMP()";
        mysqlpp::Result res = query.store();
        if (!res.empty())
        {
            for (unsigned long i = 0; i < res.num_rows(); i++)
            {
                mysqlpp::Row row = res.fetch_row();
                accounts.push_back(atol(row.raw_data(0)));
            }            
            bResult = true;
        }       
    }
    catch (const mysqlpp::Exception& exc)
    {
        LOGT(("DBLayer::GetAllNotActivatedAccounts. MySQL exception occurred. Message: %s\n", exc.what()));
        NeedReconnect(connection->errnum());
    }
    catch (std::exception& exc)
    {
        LOGT(("DBLayer::GetAllNotActivatedAccounts. Std exception occurred. Message: %s\n", exc.what()));
    }
    LeaveCriticalSection(&criticalSection);
    return bResult;
}

bool DBLayer::GetAllNotActivatedMonitors(   const unsigned long& ulPeriod,
                                            vector<accountid_monitorid>& monitors)
{
    bool bResult = false;
    EnterCriticalSection(&criticalSection);
    try
    {
        mysqlpp::Query query = connection->query();
        query << "SELECT account_id, id FROM monitors WHERE status & " << MONITOR_STATUS_APPROVED << " = 0 AND monitors.created + INTERVAL " << ulPeriod << " DAY < UTC_TIMESTAMP()";
        mysqlpp::Result res = query.store();
        if (!res.empty())
        {
            for (unsigned long i = 0; i < res.num_rows(); i++)
            {
                mysqlpp::Row row = res.fetch_row();
                monitors.push_back(make_pair(atol(row.raw_data(0)), atol(row.raw_data(1))));
            }            
            bResult = true;
        }       
    }
    catch (const mysqlpp::Exception& exc)
    {
        LOGT(("DBLayer::GetAllNotActivatedMonitors. MySQL exception occurred. Message: %s\n", exc.what()));
        NeedReconnect(connection->errnum());
    }
    catch (std::exception& exc)
    {
        LOGT(("DBLayer::GetAllNotActivatedMonitors. Std exception occurred. Message: %s\n", exc.what()));
    }
    LeaveCriticalSection(&criticalSection);
    return bResult;
}

bool DBLayer::UpdateMonitorInfo(const unsigned int& uiMonitorID,
                                const unsigned int& uiContentType,
                                const unsigned int& uiLastAlertType,
                                const string& szTitle,
                                const string& szDomainName,
                                const string& szFavicon,
                                const string& szFaviconContentType)
{
    bool bResult = false;
    EnterCriticalSection(&criticalSection);
    try
    {
        if (BeginTransaction())
        {
            mysqlpp::Query query = connection->query();
            if (uiContentType != CONTENT_TYPE_UNKNOWN)
            {
                query << "UPDATE monitors SET content_type = " << uiContentType << " WHERE id = " << uiMonitorID;
                query.execute();
            }
            if (uiLastAlertType != ALERT_TYPE_NONE)
            {
                query << "UPDATE monitors SET last_alert = UTC_TIMESTAMP(), last_alert_type = " << uiLastAlertType << " WHERE id = " << uiMonitorID;
                query.execute();
            }
            if (szTitle.size())
            {
                string szHexTitle;
                ConvertStringToHexString((const unsigned char*)szTitle.c_str(), szTitle.size(), szHexTitle);
                query << "UPDATE monitors SET title = " << szHexTitle << " WHERE id = " << uiMonitorID;
                query.execute();
            }
            if (szDomainName.size())
            {
                string szHexDomainName;
                ConvertStringToHexString((const unsigned char*)szDomainName.c_str(), szDomainName.size(), szHexDomainName);
                query << "UPDATE monitors SET domain_name = " << szHexDomainName << " WHERE id = " << uiMonitorID;
                query.execute();
            }
            if (szFavicon.size()&& szFaviconContentType.size())
            {
                string szHexFavicon;
                ConvertStringToHexString((const unsigned char*)szFavicon.c_str(), szFavicon.size(), szHexFavicon);
                string szHexFaviconContentType;
                ConvertStringToHexString((const unsigned char*)szFaviconContentType.c_str(), szFaviconContentType.size(), szHexFaviconContentType);
                query << "UPDATE monitors SET favicon = " << szHexFavicon << ", favicon_content_type = " << szHexFaviconContentType << " WHERE id = " << uiMonitorID;
                query.execute();
            }
            query << "SELECT frequency FROM monitors WHERE id = " << uiMonitorID;
            mysqlpp::Result res = query.store();
            if (!res.empty())
            {
                mysqlpp::Row row = res.fetch_row();
                unsigned int uiFreqency = atol(row.raw_data(0));
                if (uiFreqency == 86400)
                {
                    query << "UPDATE monitors SET last_update = UTC_TIMESTAMP() WHERE id = " << uiMonitorID;
                    query.execute();
                }
                else
                {
                    mysqlpp::DateTime nexttime;
                    mysqlpp::DateTime now;
                    query << "SELECT last_update + INTERVAL frequency * 2 SECOND, UTC_TIMESTAMP() FROM monitors WHERE id = " << uiMonitorID;
                    mysqlpp::Result res = query.store();
                    if (!res.empty())
                    {
                        mysqlpp::Row row = res.fetch_row();
                        nexttime = row.raw_data(0);
                        now = row.raw_data(1);
                        if(nexttime >= now)
                        {
                            query << "UPDATE monitors SET last_update = last_update + INTERVAL frequency SECOND WHERE id = " << uiMonitorID;
                            query.execute();
                        }
                        else
                        {
                            query << "UPDATE monitors SET last_update = UTC_TIMESTAMP() WHERE id = " << uiMonitorID;
                            query.execute();
                        }
                    }
                }
            }
            dbLayer->CommitTransaction();
            bResult = true;
        }
    }
    catch (const mysqlpp::Exception& exc)
    {
        LOGT(("DBLayer::UpdateMonitorInfo. MySQL exception occurred. Message: %s\n", exc.what()));
        NeedReconnect(connection->errnum());
        RollBackTransaction();
    }
    catch (std::exception& exc)
    {
        LOGT(("DBLayer::UpdateMonitorInfo. Std exception occurred. Message: %s\n", exc.what()));
        RollBackTransaction();
    }
    std::vector<unsigned int>::iterator new_end;
    new_end = remove(monitors_cache.begin(), monitors_cache.end(), uiMonitorID);
    monitors_cache.erase(new_end, monitors_cache.end());
    LeaveCriticalSection(&criticalSection);
    return bResult;
}

bool DBLayer::SetMonitorStatus( const unsigned int& uiMonitorID,
                                const unsigned int& uiMonitorStatus)
{
    bool bResult = false;
    EnterCriticalSection(&criticalSection);
    try
    {
        mysqlpp::Query query = connection->query();
        query << "UPDATE monitors SET status = " << uiMonitorStatus << " WHERE id = " << uiMonitorID;
        query.execute();
        bResult = true;
    }
    catch (const mysqlpp::Exception& exc)
    {
        LOGT(("DBLayer::SetMonitorStatus. MySQL exception occurred. Message: %s\n", exc.what()));
        NeedReconnect(connection->errnum());
    }
    catch (std::exception& exc)
    {
        LOGT(("DBLayer::SetMonitorStatus. Std exception occurred. Message: %s\n", exc.what()));
    }
    LeaveCriticalSection(&criticalSection);
    return bResult;
}

bool DBLayer::SetEmailStatus(const unsigned int& uiEmailID,
                             const unsigned int& uiEmailStatus)
{
    bool bResult = false;
    EnterCriticalSection(&criticalSection);
    try
    {
        mysqlpp::Query query = connection->query();
        query << "UPDATE emails_storage SET send_status = " << uiEmailStatus << " WHERE id = " << uiEmailID;
        query.execute();
        bResult = true;
    }
    catch (const mysqlpp::Exception& exc)
    {
        LOGT(("DBLayer::SetEmailStatus. MySQL exception occurred. Message: %s\n", exc.what()));
        NeedReconnect(connection->errnum());
    }
    catch (std::exception& exc)
    {
        LOGT(("DBLayer::SetEmailStatus. Std exception occurred. Message: %s\n", exc.what()));
    }
    std::vector<unsigned int>::iterator new_end;
    new_end = remove(emails_cache.begin(), emails_cache.end(), uiEmailID);
    emails_cache.erase(new_end, emails_cache.end());
    LeaveCriticalSection(&criticalSection);
    return bResult;
}

bool DBLayer::SaveEmailToStorage(   const unsigned int& uiMonitorID,
                                    const unsigned int& uiAccountID,
                                    string& szEmailFrom,
                                    const vector<string>& recipients,
                                    const int& iAlertAtTime,
                                    const int& iTimeZoneOffset,
                                    const string& szEmailBody,
                                    const bool& bCompress)
{
    bool bResult = false;
    EnterCriticalSection(&criticalSection);
    try
    {
        string szAlertEmails;
        vector<string>::const_iterator iter;
        for (iter = recipients.begin(); iter != recipients.end(); iter++)
        {
            szAlertEmails += *iter;
            szAlertEmails += " ";
        }
        string szHexAlertEmails;
        ConvertStringToHexString((const unsigned char*)szAlertEmails.c_str(), szAlertEmails.size(), szHexAlertEmails);
        string szHexEmailFrom;
        ConvertStringToHexString((const unsigned char*)szEmailFrom.c_str(), szEmailFrom.size(), szHexEmailFrom);

        mysqlpp::Query query = connection->query();
        query << "INSERT INTO emails_storage (account_id, monitor_id, send_status, email_from, alert_emails, email_content, email_content_size, email_content_is_compressed, send_time) ";
        query << "VALUES (";
        if (uiAccountID)
            query << uiAccountID << ", ";
        else
            query << "NULL, ";
        if (uiMonitorID)
            query << uiMonitorID << ", ";
        else
            query << "NULL, ";
        query << EMAIL_STATUS_NEED_SEND << ", " << szHexEmailFrom << ", " << szHexAlertEmails << ", ";
        if (bCompress)
        {
            vector<unsigned char> buffer;
            unsigned long ulSize = szEmailBody.size();
            buffer.resize(ulSize);
            int result = compress2(&buffer[0], &ulSize, (const Bytef*)szEmailBody.c_str(), ulSize, Z_BEST_COMPRESSION);
            if (result == Z_OK)
            {
                string szHexBuffer;
                ConvertStringToHexString(&buffer[0], ulSize, szHexBuffer);
                query << szHexBuffer << ", " << szEmailBody.size() << ", 1, ";
            }
            else
            {
                LOGT(("DBLayer::SaveEmailToStorage. Compression failed. Zlib return: %i\n", result));
                string szHexEmailContent;
                ConvertStringToHexString((const unsigned char*)szEmailBody.c_str(), szEmailBody.size(), szHexEmailContent);
                query << szHexEmailContent << ", " << szEmailBody.size() << ", 0, ";
            }
        }
        else
        {
            string szHexEmailContent;
            ConvertStringToHexString((const unsigned char*)szEmailBody.c_str(), szEmailBody.size(), szHexEmailContent);
            query << szHexEmailContent << ", " << szEmailBody.size() << ", 0, ";
        }

        if (iAlertAtTime != -1)
        {
            mysqlpp::Query querytime = connection->query();
            querytime << "SELECT UTC_DATE() - INTERVAL " << iTimeZoneOffset <<" SECOND + INTERVAL " << iAlertAtTime << " SECOND >= UTC_TIMESTAMP()";
            mysqlpp::Result res = querytime.store();
            if (!res.empty())
            {
                mysqlpp::Row row = res.fetch_row();
                unsigned long ulSendToday = atol(row.raw_data(0));
                if (ulSendToday)
                    query << "UTC_DATE() - INTERVAL " << iTimeZoneOffset << " SECOND + INTERVAL " << iAlertAtTime << " SECOND)";                    
                else
                    query << "UTC_DATE() - INTERVAL " << iTimeZoneOffset << " SECOND + INTERVAL " << iAlertAtTime << " SECOND + INTERVAL 1 DAY)";
            }
        }
        else
        {
            query << " UTC_TIMESTAMP())";
        }        
        query.execute();
        bResult = true;
    }
    catch (const mysqlpp::Exception& exc)
    {
        LOGT(("DBLayer::SaveEmailToStorage. MySQL exception occurred. Message: %s\n", exc.what()));
        NeedReconnect(connection->errnum());
    }
    catch (std::exception& exc)
    {
        LOGT(("DBLayer::SaveEmailToStorage. Std exception occurred. Message: %s\n", exc.what()));
    }
    LeaveCriticalSection(&criticalSection);
    return bResult;
}

bool DBLayer::SaveResourceToStorage(    const unsigned int& uiMonitorID,
                                        const unsigned int& uiAccountID,
                                        const unsigned int& uiTriggerType, 
                                        const string& szEffectiveUrl,
                                        const string& szResource,
                                        const bool& bCompress,
                                        const unsigned long& ulCurlCode,
                                        const string& szCurlMessage,
                                        const unsigned long& ulHttpResponseCode,
                                        const string& szHttpResponseHeader,
                                        const unsigned char& ucResourceStatus,
                                        const unsigned int& uiQtyAdded,
                                        const unsigned int& uiQtyDeleted,
                                        const unsigned int& uiQtyTriggers,
                                        const unsigned int& uiQtyAnchors,
                                        unsigned int& uiResourceID)
{    
    bool bResult = false;
    EnterCriticalSection(&criticalSection);
    try
    {
        uiResourceID = 0;

        unsigned int uiMaxHistorySize = 0;
        unsigned int uiCurHistorySize = 0;
        mysqlpp::Query query = connection->query();
        mysqlpp::Result res;

        if (BeginTransaction())
        {
            query << "SELECT plans.history_size FROM accounts, plans WHERE accounts.id = " << uiAccountID << " AND accounts.plan_id = plans.id";
            res = query.store();
            if (!res.empty())
            {
                mysqlpp::Row row = res.fetch_row();
                uiMaxHistorySize = atol(row.raw_data(0));
            }
            query << "SELECT COUNT(*) FROM resources_storage WHERE monitor_id = " << uiMonitorID << " AND resource_status <> " << RESOURCE_STORAGE_STATUS_FAKE;
            res = query.store();
            if (!res.empty())
            {
                mysqlpp::Row row = res.fetch_row();
                uiCurHistorySize = atol(row.raw_data(0));
            }

            string szHexEffectiveUrl;
            string szHexCurlMessage;
            string szHexHttpResponseHeader;
            ConvertStringToHexString((const unsigned char*)szEffectiveUrl.c_str(), szEffectiveUrl.size(), szHexEffectiveUrl);
            ConvertStringToHexString((const unsigned char*)szCurlMessage.c_str(), szCurlMessage.size(), szHexCurlMessage);
            ConvertStringToHexString((const unsigned char*)szHttpResponseHeader.c_str(), szHttpResponseHeader.size(), szHexHttpResponseHeader);

            query << "INSERT INTO resources_storage (monitor_id, download_dt, effective_url, resource, resource_src_size, resource_is_compressed, ";
            query << "curl_code, curl_message, http_response_code, http_response_header, resource_status, qty_added, qty_deleted, qty_triggers, qty_anchors) " ;
            query << "VALUES (" << uiMonitorID << ", UTC_TIMESTAMP(), " << szHexEffectiveUrl << ", ";
            if (ulCurlCode == 0 && ulHttpResponseCode == HTTP_STATUS_OK)
            {
                if (ucResourceStatus != RESOURCE_STORAGE_STATUS_NOT_CHANGED)
                {
                    if (bCompress)
                    {
                        vector<unsigned char> buffer;
                        unsigned long ulResourceSize = szResource.size();
                        buffer.resize(ulResourceSize);
                        int result = compress2(&buffer[0], &ulResourceSize, (const Bytef*)szResource.c_str(), ulResourceSize, Z_BEST_COMPRESSION);
                        if (result == Z_OK)
                        {
                            string szHexBuffer;
                            ConvertStringToHexString(&buffer[0], ulResourceSize, szHexBuffer);
                            query << szHexBuffer << ", " << szResource.size() << ", 1, " << ulCurlCode << ", " << szHexCurlMessage << ", ";
                            query << ulHttpResponseCode << ", " << szHexHttpResponseHeader << ", ";
                        }
                        else
                        {
                            LOGT(("DBLayer::SaveResourceToStorage. Compression failed. Zlib return: %i\n", result));
                            string szHexHtml;
                            ConvertStringToHexString((const unsigned char*)szResource.c_str(), szResource.size(), szHexHtml);
                            query << szHexHtml << ", " << szResource.size() << ", 0, " << ulCurlCode << ", " << szHexCurlMessage << ", ";
                            query << ulHttpResponseCode << ", " << szHexHttpResponseHeader << ", ";
                        }
                    }
                    else
                    {
                        string szHexHtml;
                        ConvertStringToHexString((const unsigned char*)szResource.c_str(), szResource.size(), szHexHtml);
                        query << szHexHtml << ", " << szResource.size() << ", 0, " << ulCurlCode << ", " << szHexCurlMessage << ", ";
                        query << ulHttpResponseCode << ", " << szHexHttpResponseHeader << ", ";
                    }
                }
                else
                {
                    query << "NULL, 0, 0, " << ulCurlCode << ", " << szHexCurlMessage << ", ";
                    query << ulHttpResponseCode << ", " << szHexHttpResponseHeader << ", ";
                }
            }
            else
            if (ulCurlCode == 0 && ulHttpResponseCode != HTTP_STATUS_OK)
            {
                query << "NULL, 0, 0, " << ulCurlCode << ", " << szHexCurlMessage << ", ";
                query << ulHttpResponseCode << ", " << szHexHttpResponseHeader << ", ";
            }
            else
            {
                query << "NULL, 0, 0, " << ulCurlCode << ", " << szHexCurlMessage << ", ";
                query << "NULL, NULL, ";
            }
            query << "%0, " << uiQtyAdded << ", " << uiQtyDeleted << ", " << uiQtyTriggers << ", " << uiQtyAnchors << ")";
            query.parse();
            if (uiCurHistorySize)
            {
                query.execute((unsigned int)ucResourceStatus);
            }
            else
            {
                query.execute((unsigned int)RESOURCE_STORAGE_STATUS_FAKE);
                query.execute((unsigned int)ucResourceStatus);
                if ((uiTriggerType & 0x0000FFFF) != TRIGGER_ANY_CHANGES)
                {
                    query.reset();
                    query << "UPDATE resources_storage SET resource = '', resource_src_size = 0 WHERE id = (SELECT LAST_INSERT_ID())-1";
                    query.execute();
                }
            }
            uiCurHistorySize++;
            query.reset();
            query << "SELECT LAST_INSERT_ID()";
            mysqlpp::Result res = query.store();
            if (!res.empty())
            {
                mysqlpp::Row row = res.fetch_row();
                uiResourceID = atol(row.raw_data(0));
            }            
            if (uiCurHistorySize > uiMaxHistorySize)
            {
                query << "SELECT id, resource_status FROM resources_storage WHERE resource_status <> " << RESOURCE_STORAGE_STATUS_FAKE;
                query << " AND monitor_id = " << uiMonitorID << " ORDER BY download_dt DESC";
                res = query.store();
                if (!res.empty())
                {
                    for (unsigned int i = 1; i <= uiCurHistorySize; i++)
                    {
                        mysqlpp::Row row = res.fetch_row();
                        if (i > uiMaxHistorySize)
                        {
                            unsigned int uiLastNonFakeResourceID = atol(row.raw_data(0));
                            unsigned char ucLastNonFakeResourceStatus = (unsigned char)atol(row.raw_data(1));
                            if (ucLastNonFakeResourceStatus == RESOURCE_STORAGE_STATUS_ERROR || 
                                ucLastNonFakeResourceStatus == RESOURCE_STORAGE_STATUS_NOT_CHANGED)
                            {
                                bool bFinded = false;
                                query << "DELETE FROM resources_storage WHERE id = " << uiLastNonFakeResourceID;
                                query.execute();
                                for (unsigned int j = i+1; j <= uiCurHistorySize; j++)
                                {
                                    row = res.fetch_row();
                                    unsigned int uiCurNonFakeResourceID = atol(row.raw_data(0));
                                    unsigned char ucCurNonFakeResourceStatus = (unsigned char)atol(row.raw_data(1));
                                    if (ucCurNonFakeResourceStatus == RESOURCE_STORAGE_STATUS_ERROR || 
                                        ucCurNonFakeResourceStatus == RESOURCE_STORAGE_STATUS_NOT_CHANGED || bFinded)
                                    {
                                        query << "DELETE FROM resources_storage WHERE id = " << uiCurNonFakeResourceID;
                                        query.execute();
                                    }
                                    else
                                    {
                                        query << "DELETE FROM resources_storage WHERE resource_status = " << RESOURCE_STORAGE_STATUS_FAKE;
                                        query << " AND monitor_id = " << uiMonitorID;
                                        query.execute();
                                        query << "UPDATE resources_storage SET resource_status = " << RESOURCE_STORAGE_STATUS_FAKE;
                                        query << " WHERE id = " << uiCurNonFakeResourceID;
                                        query.execute();
                                        bFinded = true;
                                    }
                                }
                            }
                            else
                            {
                                for (unsigned int j = i+1; j <= uiCurHistorySize; j++)
                                {
                                    row = res.fetch_row();
                                    query << "DELETE FROM resources_storage WHERE id = " << row.raw_data(0);
                                    query.execute();                                                                        
                                }
                                query << "DELETE FROM resources_storage WHERE resource_status = " << RESOURCE_STORAGE_STATUS_FAKE;
                                query << " AND monitor_id = " << uiMonitorID;
                                query.execute();
                                query << "UPDATE resources_storage SET resource_status = " << RESOURCE_STORAGE_STATUS_FAKE;
                                query << " WHERE id = " << uiLastNonFakeResourceID;
                                query.execute();
                            }
                            break;
                        }
                    }
                }
            }
            bResult = true;
            CommitTransaction();
        }
    }
    catch (const mysqlpp::Exception& exc)
    {
        LOGT(("DBLayer::SaveResourceToStorage. MySQL exception occurred. Message: %s\n", exc.what()));
        NeedReconnect(connection->errnum());
        RollBackTransaction();
    }
    catch (std::exception& exc)
    {
        LOGT(("DBLayer::SaveResourceToStorage. Std exception occurred. Message: %s\n", exc.what()));
        RollBackTransaction();
    }
    LeaveCriticalSection(&criticalSection);
    return bResult;
}

bool DBLayer::SaveDownloadStatistics(   const unsigned int& uiMonitorID,
                                        const unsigned int& uiAccountID,
                                        const double& dTotalTime,
                                        const double& dDownloadSpeed,
                                        const unsigned int& uiDownloadSize,
                                        const unsigned char& ucResourceStatus)
{
    bool bResult = false;
    EnterCriticalSection(&criticalSection);
    try
    {
        mysqlpp::Query query = connection->query();
        query << "INSERT INTO download_stats (account_id, monitor_id, download_dt, ";
        query << "total_time, speed_of_download, size_of_download, resource_status) VALUES (";
        query << uiAccountID << ", " << uiMonitorID << ", UTC_TIMESTAMP(), ";
        query << dTotalTime << ", " << dDownloadSpeed << ", " << uiDownloadSize << ", " << (unsigned int)ucResourceStatus << ")";
        query.execute();
        bResult = true;
    }
    catch (const mysqlpp::Exception& exc)
    {
        LOGT(("DBLayer::SaveDownloadStatistics. MySQL exception occurred. Message: %s\n", exc.what()));
        NeedReconnect(connection->errnum());
    }
    catch (std::exception& exc)
    {
        LOGT(("DBLayer::SaveDownloadStatistics. Std exception occurred. Message: %s\n", exc.what()));
    }
    LeaveCriticalSection(&criticalSection);
    return bResult;
}

bool DBLayer::SaveDiffCache(    const unsigned int& uiResourceID1,
                                const unsigned int& uiResourceID2,
                                const string& szDiffCache,
                                const double& dTotalTime,
                                const unsigned int& uiVersion)
{
    bool bResult = false;
    EnterCriticalSection(&criticalSection);
    try
    {
        mysqlpp::Query query = connection->query();
        query << "INSERT INTO diff_cache (resource1_id, resource2_id, value, total_time, diff_version) ";
        query << "VALUES (" << uiResourceID1 << ", " << uiResourceID2 << ", '" << szDiffCache << "', " << dTotalTime << ", " << uiVersion << ")";
        query.execute();
        bResult = true;
    }
    catch (const mysqlpp::Exception& exc)
    {
        LOGT(("DBLayer::SaveDiffCache. MySQL exception occurred. Message: %s\n", exc.what()));
        NeedReconnect(connection->errnum());
    }
    catch (std::exception& exc)
    {
        LOGT(("DBLayer::SaveDiffCache. Std exception occurred. Message: %s\n", exc.what()));
    }
    LeaveCriticalSection(&criticalSection);
    return bResult;
}

bool DBLayer::SaveFeed( const unsigned int& uiMonitorID,
                        const unsigned int& uiFeedType,
                        const string& szFeed)
{
    bool bResult = false;
    EnterCriticalSection(&criticalSection);
    try
    {
        mysqlpp::Query query = connection->query();
        string szHexFeed;
        ConvertStringToHexString((const unsigned char*)szFeed.c_str(), szFeed.size(), szHexFeed);
        query << "SELECT id FROM feeds WHERE monitor_id = " << uiMonitorID << " AND type = " << uiFeedType;
        mysqlpp::Result res = query.store();
        if (res.empty())
        {
            query << "INSERT INTO feeds (monitor_id, type, feed) VALUES (" << uiMonitorID << ", " << uiFeedType << ", " << szHexFeed << ")";            
        }
        else
        {
            query << "UPDATE feeds SET feed = " << szHexFeed << " WHERE monitor_id = " << uiMonitorID << " AND type = " << uiFeedType;
        }
        query.execute();
        bResult = true;
    }
    catch (const mysqlpp::Exception& exc)
    {
        LOGT(("DBLayer::SaveFeed. MySQL exception occurred. Message: %s\n", exc.what()));
        NeedReconnect(connection->errnum());
    }
    catch (std::exception& exc)
    {
        LOGT(("DBLayer::SaveFeed. Std exception occurred. Message: %s\n", exc.what()));
    }
    LeaveCriticalSection(&criticalSection);
    return bResult;
}

bool DBLayer::NeedDeactivateMonitorByCurlCode(  const unsigned int& uiMonitorID,
                                                const unsigned long& ulCurlCode,
                                                const unsigned long& ulMaxErrorsCount,
                                                bool& bNeedDeactivate)
{
    bool bResult = false;
    EnterCriticalSection(&criticalSection);
    bNeedDeactivate = false;
    try
    {
        mysqlpp::Query query = connection->query();
        query << "SELECT curl_code FROM resources_storage";
        query << " WHERE monitor_id = " << uiMonitorID << " AND resource_status <> " << RESOURCE_STORAGE_STATUS_FAKE;
        query << " ORDER BY download_dt DESC LIMIT " << ulMaxErrorsCount;
        mysqlpp::Result res = query.store();
        if (!res.empty())
        {
            if (res.num_rows() == ulMaxErrorsCount)
            {
                bool bAllEqual = true;
                for (unsigned long i = 0; i < ulMaxErrorsCount; i++)
                {
                    mysqlpp::Row row = res.at(i);
                    unsigned long ulCode = atol(row.raw_data(0));
                    if (ulCode != ulCurlCode)
                    {
                        bAllEqual = false;
                        break;
                    }
                }
                if (bAllEqual)
                    bNeedDeactivate = true;
            }
        }
        bResult = true;
    }
    catch (const mysqlpp::Exception& exc)
    {
        LOGT(("DBLayer::NeedDeactivateMonitorByCurlCode. MySQL exception occurred. Message: %s\n", exc.what()));
        NeedReconnect(connection->errnum());
    }
    catch (std::exception& exc)
    {
        LOGT(("DBLayer::NeedDeactivateMonitorByCurlCode. Std exception occurred. Message: %s\n", exc.what()));
    }
    LeaveCriticalSection(&criticalSection);
    return bResult;
}

bool DBLayer::NeedDeactivateMonitorByResponseCode(  const unsigned int& uiMonitorID,
                                                    const unsigned long& ulResponseCode,
                                                    const unsigned long& ulMaxErrorsCount,
                                                    bool& bNeedDeactivate)
{
    bool bResult = false;    
    EnterCriticalSection(&criticalSection);
    bNeedDeactivate = false;
    try
    {
        mysqlpp::Query query = connection->query();
        query << "SELECT http_response_code FROM resources_storage";
        query << " WHERE monitor_id = " << uiMonitorID << " AND resource_status <> " << RESOURCE_STORAGE_STATUS_FAKE;
        query << " ORDER BY download_dt DESC LIMIT " << ulMaxErrorsCount;
        mysqlpp::Result res = query.store();
        if (!res.empty())
        {
            if (res.num_rows() == ulMaxErrorsCount)
            {
                bool bAllEqual = true;
                for (unsigned long i = 0; i < ulMaxErrorsCount; i++)
                {
                    mysqlpp::Row row = res.at(i);
                    unsigned long ulCode = atol(row.raw_data(0));
                    if (ulCode != ulResponseCode)
                    {
                        bAllEqual = false;
                        break;
                    }
                }
                if (bAllEqual)
                    bNeedDeactivate = true;
            }
        }
        bResult = true;
    }
    catch (const mysqlpp::Exception& exc)
    {
        LOGT(("DBLayer::NeedDeactivateMonitorByResponseCode. MySQL exception occurred. Message: %s\n", exc.what()));
        NeedReconnect(connection->errnum());
    }
    catch (std::exception& exc)
    {
        LOGT(("DBLayer::NeedDeactivateMonitorByResponseCode. Std exception occurred. Message: %s\n", exc.what()));
    }
    LeaveCriticalSection(&criticalSection);
    return bResult;
}

bool DBLayer::ClearEmailStorage(const unsigned long& ulPeriod)
{
    bool bResult = false;
    EnterCriticalSection(&criticalSection);
    try
    {
        mysqlpp::Query query = connection->query();
        query << "DELETE FROM emails_storage WHERE send_time < UTC_TIMESTAMP() - INTERVAL " << ulPeriod << " DAY";
        query << " AND send_status <> " << EMAIL_STATUS_NEED_SEND;
        mysqlpp::ResNSel res = query.execute();
        LOGT(("DBLayer::ClearEmailStorage. Clearing emails storage completed. Deleted %I64u emails\n", res.rows));
        bResult = true;
    }
    catch (const mysqlpp::Exception& exc)
    {
        LOGT(("DBLayer::ClearEmailStorage. MySQL exception occurred. Message: %s\n", exc.what()));
        NeedReconnect(connection->errnum());
    }
    catch (std::exception& exc)
    {
        LOGT(("DBLayer::ClearEmailStorage. Std exception occurred. Message: %s\n", exc.what()));
    }
    LeaveCriticalSection(&criticalSection);
    return bResult;
}

bool DBLayer::ClearDiffCache( const unsigned int& uiDiffVersion )
{
    bool bResult = false;
    EnterCriticalSection(&criticalSection);
    try
    {
        mysqlpp::Query query = connection->query();
        query << "DELETE FROM diff_cache WHERE diff_version <> " << uiDiffVersion;
        mysqlpp::ResNSel res = query.execute();
        LOGT(("DBLayer::ClearDiffCache. Clearing diff cache. Deleted %I64u items\n", res.rows));
        bResult = true;
    }
    catch (const mysqlpp::Exception& exc)
    {
        LOGT(("DBLayer::ClearDiffCache. MySQL exception occurred. Message: %s\n", exc.what()));
        NeedReconnect(connection->errnum());
    }
    catch (std::exception& exc)
    {
        LOGT(("DBLayer::ClearDiffCache. Std exception occurred. Message: %s\n", exc.what()));
    }
    LeaveCriticalSection(&criticalSection);
    return bResult;
}

bool DBLayer::DeleteAccount(const unsigned int& uiAccountID)
{
    bool bResult = false;
    EnterCriticalSection(&criticalSection);
    try
    {
        mysqlpp::Query query = connection->query();
        query << "DELETE FROM accounts WHERE id = " << uiAccountID;
        mysqlpp::ResNSel res = query.execute();
        if (res.rows)
            LOGT(("DBLayer::DeleteAccount. AccountID %u deleted\n", uiAccountID));
        bResult = true;
    }
    catch (const mysqlpp::Exception& exc)
    {
        LOGT(("DBLayer::DeleteAccount. MySQL exception occurred. Message: %s\n", exc.what()));
        NeedReconnect(connection->errnum());
    }
    catch (std::exception& exc)
    {
        LOGT(("DBLayer::DeleteAccount. Std exception occurred. Message: %s\n", exc.what()));
    }
    LeaveCriticalSection(&criticalSection);
    return bResult;
}

bool DBLayer::DeleteMonitor(const unsigned int& uiMonitorID)
{
    bool bResult = false;
    EnterCriticalSection(&criticalSection);
    try
    {
        mysqlpp::Query query = connection->query();
        query << "DELETE FROM monitors WHERE id = " << uiMonitorID;
        mysqlpp::ResNSel res = query.execute();
        if (res.rows)
            LOGT(("DBLayer::DeleteMonitor. MonitorID %u deleted\n", uiMonitorID));
        bResult = true;
    }
    catch (const mysqlpp::Exception& exc)
    {
        LOGT(("DBLayer::DeleteMonitor. MySQL exception occurred. Message: %s\n", exc.what()));
        NeedReconnect(connection->errnum());
    }
    catch (std::exception& exc)
    {
        LOGT(("DBLayer::DeleteAccount. Std exception occurred. Message: %s\n", exc.what()));
    }
    LeaveCriticalSection(&criticalSection);
    return bResult;
}